package skyblue;

import java.util.LinkedList;
import java.util.Stack;

import featuremodel.FeatureRep;
import featuremodel.method.MethodChecker;

public class MVine {

	private SkyBlue system;
	private Mark doneMark;
	private boolean ok;
	private Stack<Constraint> cnstack;

	public MVine(SkyBlue system, Constraint cn) {

		this.system = system;
		this.doneMark = new Mark();
		this.cnstack = new Stack<Constraint>();
		this.ok = build(cn);

	}

	public MVine(MVine mVine) {
		this.system = mVine.getSystem();
		this.doneMark = mVine.getDonemark();
		this.ok = mVine.isOk();
		this.cnstack = mVine.getCnstack();
	}

	public boolean build(Constraint cn) {

		cnstack.clear();
		return enforceConstraint(cn, cn.getStrength());
	}

	public boolean grow(Strength rootstrength) {
		if (cnstack.isEmpty())
			return true;

		boolean ok;
		//System.out.print("The number of the cn stack is: "+cnstack.size()+"\n");
		Constraint cn = cnstack.pop();
		// System.out.print("在grow函数里面" + cnstack.size() + "\n");
		// Q 锞婏降锞忥緰锝碉緞锝猴緧锞庯奖锝存儾锟斤交锞掞交锞戯拷
		if (cn.getMark().getMyMark() == getDonemark().getMyMark()) {
			ok = grow(rootstrength);
		} else if (cn.getStrength().weaker(rootstrength)) {

			ok = revokeConstraint(cn, rootstrength);
		} else {
			ok = enforceConstraint(cn, rootstrength);
		}

		if (ok == false)
			cnstack.push(cn);

		return ok;

	}

	public boolean revokeConstraint(Constraint cn, Strength rootstrength) {

		boolean ok;
		cn.setMark(getDonemark());
		ok = grow(rootstrength);
		// let grow handle all the other constraints and if it returns true, it
		// means that a mvine is successfully constructed
		if (ok) {
			FeatureRep f;
			System.out.print("constriant \"" + cn.getConstraintType() + "(");
			for (int q = 0; q < cn.getVariables().size(); q++) {
				f = (FeatureRep) cn.getVariables().get(q).getValue()
						.getValuerep();
				System.out.print(f.getName() + ",");
			}
			System.out.println(")\" is revoked.");

			Variable iteri;

			for (int i = 0; i < cn.getSelectedMethod().getOutputs().size(); i++) {
				iteri = cn.getSelectedMethod().getOutputs().get(i);
				// the variable is set doneMark only a method is found and the
				// method determines it
				iteri.getDeterminedbys().remove(cn);
				// if (iteri.getMark().getMyMark() != getDonemark().getMyMark())
				// {
				// if (cn.equals(iteri.getStrongestcn())) {
				// iteri.setTobeMethodType2(iteri.getTobemethodtype());
				// iteri.setWalkStrength(SkyBlueDef.WEAKEST_STRENGTH);
				if (iteri.getDeterminedbys().size() > 0) {
					// caculateWalkaboutStrength(iteri);
					system.getSubundetVars().add(iteri);
				} else {
					system.getUndetVars().add(iteri);
				}
				system.addExecRoot(iteri);
				// }
				// }
			}

			// system.addExecRoot(cn);
			cn.revoke();
			return true;
		} else {
			cn.getMark().clear();
			return false;
		}
	}

	public boolean enforceConstraint(Constraint cn, Strength rootstrength) {
//		FeatureRep f;
//		System.out.print("trying to enforce constriant \""
//				+ cn.getConstraintType() + "(");
//		for (int q = 0; q < cn.getVariables().size(); q++) {
//			f = (FeatureRep) cn.getVariables().get(q).getValue().getValuerep();
//			System.out.print(f.getName() + ",");
//		}
//		System.out.println(")\" ");

		LinkedList<Variable> oldoutputs = new LinkedList<Variable>();
		LinkedList<Constraint> nextcns = null;
		Method iteri = null;
		Variable iterj = null;
		Constraint iterl = null;
		boolean ok;

		cn.setMark(getDonemark());

		if (cn.getSelectedMethod() != null)
			oldoutputs = cn.getSelectedMethod().getOutputs();

		for (int i = 0; i < cn.getMethods().size(); i++) {
			iteri = cn.getMethods().get(i);
			if (!iteri.equals(cn.getSelectedMethod())) {
				if (possibleMethod(iteri, rootstrength, oldoutputs)) {
					iteri.setTobeMethodType();
					//System.out.print(iteri.getClass().getName() + "\n");
					int count = 0;
					for (int j = 0; j < iteri.getOutputs().size(); j++) {

						// FM
						// 锝斤将锞旓江锞�酱锝碉緞锝碉緝锝碉浇nextcn锝革緞锞庯姜锝碉緝锝碉浇cns,锝帮緫锞擄拷顪健锝碉緞method锝碉緞ouputs锝宠锝伙降锞刢onstraint锞曪緬锝愁摟锝�
						if (iteri.getOutputs().get(j).getMark().getMyMark() != this
								.getDonemark().getMyMark()) {
							nextcns = iteri.getOutputs().get(j)
									.getDeterminedbys();
							if (MethodChecker.checkConflicts(iteri.getOutputs()
									.get(j))) {
								for (int l = 0; l < nextcns.size(); l++) {
									Constraint nextcn = nextcns.get(l);
									if (nextcn != null
											&& nextcn.getMark().getMyMark() != getDonemark()
													.getMyMark()) {
//										if(cnstack.contains(nextcn)){
//											cnstack.remove(nextcn);
//										}
										cnstack.push(nextcn);
//										System.out.print(nextcn
//												.getConstraintType()
//												+ "is added \n");
										count++;

									}

								}

								// while (!nextcns.isEmpty()) {
								// Constraint nextcn = nextcns.getLast();
								// if (nextcn != null) {
								// cnstack.push(nextcn);
								// count++;
								// }
								// }
							}

						}

						iteri.getOutputs().get(j).setMark(getDonemark());
						iteri.getOutputs().get(j).setMarkcount(
								iteri.getOutputs().get(j).getMarkcount() + 1);

					}
					// System.out.print("刚加了" + count + "个约束\n");
					// System.out.print("目前cnstack中的constraints为：");
					// for (int q = 0; q < cnstack.size(); q++) {
					// System.out.print(cnstack.get(q).getConstraintType()
					// + "  ");
					// }
					// System.out.print("\n");

					ok = grow(rootstrength);

					if (ok) {

						if (cn.getSelectedMethod() != null) {
							for (int j = 0; j < oldoutputs.size(); j++) {
								iterj = oldoutputs.get(j);
								// 锞旑櫇锝奖锝伙緯绡嬵拫n determine锝ｏ浆锞忥緰锞旓練undtermine
								if (iterj.getMark().getMyMark() != getDonemark()
										.getMyMark()) {
									iterj.getDeterminedbys().remove(cn);
									// if (cn.equals(iterj.getStrongestcn())) {
									//
									// iterj
									// .setWalkStrength(SkyBlueDef.WEAKEST_STRENGTH);
									if (iterj.getDeterminedbys().size() > 0) {
										// caculateWalkaboutStrength(iterj);
										system.getSubundetVars().add(iterj);
									} else {
										system.getUndetVars().add(iterj);
									}
									system.addExecRoot(iterj);
									// }
								} else if (!iteri.getOutputs().contains(iterj)) {
									iterj.getDeterminedbys().remove(cn);
									if (iterj.getDeterminedbys().size() > 0) {
										// caculateWalkaboutStrength(iterj);
										system.getSubundetVars().add(iterj);
									} else {
										system.getUndetVars().add(iterj);
									}
								}

							}
						}
						for (int k = 0; k < iteri.getOutputs().size(); k++) {
							iteri.getOutputs().get(k)
									.setMarkcount(
											iteri.getOutputs().get(k)
													.getMarkcount() - 1);
						}
						system.addExecRoot(cn);
						cn.enforce(iteri);
						cn.determineVars();

						return true;
					} else {

						// iteri.clearTobeMethodType();
						for (int k = 0; k < iteri.getOutputs().size(); k++) {
							iteri.getOutputs().get(k)
									.setMarkcount(
											iteri.getOutputs().get(k)
													.getMarkcount() - 1);
							if (iteri.getOutputs().get(k).getMarkcount() == 0) {
								iteri.getOutputs().get(k).getMark().clear();
								iteri.getOutputs().get(k).setTobemethodtype(
										null);
							}
						}
						// System.out.print(count + "\n");
						// System.out.print(cnstack.size() + "\n");
						for (int l = 0; l < count; l++) {
							cnstack.pop();
						}
						// for (int q = 0; q < cnstack.size(); q++) {
						// System.out.print("*******************");
						// System.out.print(cnstack.get(q).getConstraintType()
						// + "  ");
						// }
						// System.out.print("\n");
					}
				}
			}

		}
		cn.getMark().clear();
		return false;

	}

	public boolean possibleMethod(Method mt, Strength rootstrength,
			LinkedList<Variable> oldoutputs) {

		Variable iteri;
		mt.setTobeMethodType2();
		for (int i = 0; i < mt.getOutputs().size(); i++) {
			iteri = mt.getOutputs().get(i);
			// FM Conflict : 1)Be marked(processed and is a output in the mvine)
			// 2)the MethodType conflicts

			if (iteri.getMark().getMyMark() == doneMark.getMyMark()) {
				if (MethodChecker.checkBeforeConflicts2(iteri)) {
					mt.clearTobeMethodType2();
					return false;
				}
			}

			// if (!iteri.getWalkStrength().weaker(rootstrength)) {
			// // If the stronger variable is currently determine, it means
			// // that I
			// // do not have to revoke it! Even if I changed the method.
			// if (!oldoutputs.isEmpty()) {
			// if (!oldoutputs.contains(iteri)) {
			// return false;
			// }
			// } else {
			// return false;
			// }
			// }
		}
		mt.clearTobeMethodType2();
		return true;

	}

	// public void caculateWalkaboutStrength(Variable var) {
	// Constraint cn;
	// Strength newStrength;
	// Strength mtStrength;
	// Method iterj;
	// LinkedList<Variable> currentOutputs;
	// LinkedList<Constraint> cns = new LinkedList<Constraint>();
	//
	// for (int i = 0; i < var.getDeterminedbys().size(); i++) {
	// cn = var.getDeterminedbys().get(i);
	// currentOutputs = cn.getSelectedMethod().getOutputs();
	// newStrength = cn.getStrength();
	// for (int j = 0; j < cn.getMethods().size(); j++) {
	// iterj = cn.getMethods().get(j);
	//
	// iterj.setTobeMethodTyep2();
	//
	// if (!iterj.getOutputs().contains(var)) {
	// mtStrength = maxOut(iterj, currentOutputs);
	// if (mtStrength.weaker(newStrength))
	// newStrength = mtStrength;
	//
	// } else {
	// if (MethodChecker.checkBeforeConflicts2(var)) {
	// currentOutputs.remove(var);
	// mtStrength = maxOut(iterj, currentOutputs);
	// if (mtStrength.weaker(newStrength))
	// newStrength = mtStrength;
	// }
	// }
	// iterj.clearTobeMethodType2();
	// }
	//
	// cns = var.getDeterminedbys();
	// if (cns.size() == 1) {
	// var.setWalkStrength(newStrength);
	// var.setStrongestcn(cn);
	// } else if (cns.size() > 1) {
	// if (var.getWalkStrength().weaker(newStrength)) {
	// var.setWalkStrength(newStrength);
	// var.setStrongestcn(cn);
	// }
	// }
	// }
	//
	// }
	//
	// public Strength maxOut(Method iterj, LinkedList<Variable> currentOutputs)
	// {
	// Strength maxStrength = SkyBlueDef.WEAKEST_STRENGTH;
	// Variable iteri = new Variable();
	//
	// for (int i = 0; i < iterj.getOutputs().size(); i++) {
	// iteri = iterj.getOutputs().get(i);
	// if (!currentOutputs.contains(iteri)) {
	// if (MethodChecker.checkBeforeConflicts2(iteri)) {
	// if (maxStrength.weaker(iteri.getWalkStrength()))
	// maxStrength = iteri.getWalkStrength();
	// }
	//
	// }
	// }
	// return maxStrength;
	// }

	public SkyBlue getSystem() {
		return system;
	}

	public void setSystem(SkyBlue system) {
		this.system = system;
	}

	public Mark getDonemark() {
		return doneMark;
	}

	public void setDonemark(Mark donemark) {
		this.doneMark = donemark;
	}

	public boolean isOk() {
		return ok;
	}

	public void setOk(boolean ok) {
		this.ok = ok;
	}

	public Stack<Constraint> getCnstack() {
		return cnstack;
	}

	public void setCnstack(Stack<Constraint> cnstack) {
		this.cnstack = cnstack;
	}

}
